/**
 * @class   wosh::LogContext
 * @brief   Adapter to the custom/system log. Store local settings and forward
 *          calls to current (internal) LogHandler.
 * 
 * @see     wosh::LogHandler
 ****************************************************************************
 * @version 0.8.499 $Id: LogContext.h 2634 2010-06-09 23:01:02Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Core_LogContext_H__
 #define __WOSH_Core_LogContext_H__

 #include <woshDefs.h>
 #include <core/Reflection.h>
 #include <core/Containers.h>


namespace wosh {

/// represent severity of a log entry, 1 byte. also defines some log-mask
enum LogLevel {
	LOG_UNKNOWN		= 0x00,	///< unknown severity
	LOG_NONE		= 0x01,	///< mask, used to set the minimin log level. it won't log anything
	LOG_CRITICAL	= 0x02,	///< the worst error case, usually means the procedure totally failed
	LOG_WARNING		= 0x03,	///< warning message, useful to be inspected when debugging the system
	LOG_INFO		= 0x04,	///< information level, won't probably flood 
	LOG_VERBOSE		= 0x05,	///< more verbose information, will flood
	LOG_DEBUG		= 0x06,	///< max verbose information, will flood a lot
	LOG_ALL			= 0x07,	///< mask, used to set the minimin log level. it will log everything
};

/**
 * @brief  Forward call to DefaultSharedLogHandler. Provided for convenience.
 * @param level [in] LogLevel of the message
 * @param context [in] c string representing the log-context, may be NULL
 * @param format [in] c string that contains the text to be written
 * @param ... [in] depending on the format string, the function may expect a sequence of additional arguments
 * @see    DefaultSharedLogHandler::log()
 */
void log( LogLevel level, const char* context, const char* format, ... );

class LogHandler;
class SystemLogger;
template <class T> class DataFieldCollector;

#define _LogContext_CONTEXT_MAX_SIZE	31

class LogContext : public virtual wosh::IReflection
 {
	WOSH_CLASSNAME(wosh::LogContext)
	WOSH_CLASS_ABSTRACT(false)


	public:
		/**
		 * @brief  Default constructor. Register instance to global list (reentrant) and set default LogHandler.
		 * @see    LogContext::getInstances()
		 * @see    DefaultSharedLogHandler::getInstance()
		 */
		LogContext();
		/**
		 * @brief  Destructor. Unregister instance from global list (reentrant)
		 * @see    LogContext::getInstances()
		 */
		virtual ~LogContext();

	public:
		/**
		 * @brief   Log a generic message (using same format as printf). Reentrant. Usage:
		 *  \code
		 *  LogContext Log;
		 *  Log(LOG_WARNING, ":myFunction() Plain message!" );
		 *  Log(LOG_WARNING, ":myFunction(%s) Evaluating against value %d", my_std_string.c_str(), int_value );
		 *  \endcode
		 * @param level [in] LogLevel of the message
		 * @param format [in] c string that contains the text to be written
		 * @param ... [in] depending on the format string, the function may expect a sequence of additional arguments
		 * @see     http://www.cplusplus.com/reference/clibrary/cstdio/printf/
		 */
		void operator() ( LogLevel level, const char* format, ... );

		/**
		 * @brief   Log a debug (very verbose) message (using same format as printf). Reentrant.
		 * @param format [in] c string that contains the text to be written
		 * @param ... [in] depending on the format string, the function may expect a sequence of additional arguments
		 * @see     http://www.cplusplus.com/reference/clibrary/cstdio/printf/
		 */
		void debug( const char* format, ... );
		/**
		 * @brief   Log a verbose message (using same format as printf). Reentrant.
		 * @param format [in] c string that contains the text to be written
		 * @param ... [in] depending on the format string, the function may expect a sequence of additional arguments
		 * @see     http://www.cplusplus.com/reference/clibrary/cstdio/printf/
		 */
		void verbose( const char* format, ... );
		/**
		 * @brief   Log an information message (using same format as printf). Reentrant.
		 * @param format [in] c string that contains the text to be written
		 * @param ... [in] depending on the format string, the function may expect a sequence of additional arguments
		 * @see     http://www.cplusplus.com/reference/clibrary/cstdio/printf/
		 */
		void info( const char *format , ... );
		/**
		 * @brief   Log a warning message (using same format as printf). Reentrant.
		 * @param format [in] c string that contains the text to be written
		 * @param ... [in] depending on the format string, the function may expect a sequence of additional arguments
		 * @see     http://www.cplusplus.com/reference/clibrary/cstdio/printf/
		 */
		void warn( const char *format , ... );
		/**
		 * @brief   Log a critical message (using same format as printf). Reentrant.
		 * @param format [in] c string that contains the text to be written
		 * @param ... [in] depending on the format string, the function may expect a sequence of additional arguments
		 * @see     http://www.cplusplus.com/reference/clibrary/cstdio/printf/
		 */
		void critical( const char *format , ... );

//@}
/** @name Setters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief   Set log-context name. Reentrant.
		 * @param name [in] literal string representing current log context (without indentation)
		 * @param printContext [in] when true (default) it will print the log-context (with indentation)
		 */
		void setContext( const std::string& name, bool printContext = true );
		/**
		 * @brief   Set indentation level, as number of spaces before log-context. Reentrant.
		 * @param spaces [in] number of spaces of the indentation (written before log-context)
		 */
		void setIndent( short spaces );
		/**
		 * @brief   Set the minimum recorded log-level. Reentrant.
		 * @param min_level [in] mimimum (included) LogLevel which is rendered, it may be a LogLEvel mask (as LOG_ALL)
		 */
		void setLevel( LogLevel min_level );
		/**
		 * @brief   Set if the log-context (including indentation) will be printed. Reentrant.
		 * @param enabled [in] flag: when true, log-context is passed to LogHanlder; else NULL will be passed
		 */
		void setContextPrint( bool enabled );
		/**
		 * @brief   Set (replace) the current LogHandler. Reentrant.
		 * @param handler [in] handler's pointer to be set
		 */
		void setHandler( LogHandler* handler );

//@}
/** @name Getters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief   Get log-context name. Reentrant.
		 * @return  literal string representing current log context (without indentation)
		 */
		inline const std::string& getContext() const {
			MutexLockerRead mL(&this->configMux);
			return this->contextShadow;
		 }
		/**
		 * @brief   Get indentation level, as number of spaces before log-context. Reentrant.
		 * @return  number of spaces of the indentation (written before log-context)
		 */
		inline short getIndent() const {
			MutexLockerRead mL(&this->configMux);
			return this->indent;
		 }
		/**
		 * @brief   Get the minimum recorded log-level. Reentrant.
		 * @return  mimimum (included) LogLevel which is rendered.
		 */
		inline LogLevel getLevel() const {
			MutexLockerRead mL(&this->configMux);
			return this->minLevel;
		 }
		/**
		 * @brief   Get log-level as a literal string. Reentrant.
		 * @return  current log level as literal string
		 */
		inline const char* getLogLevelAsString() const { return getLogLevelAsString(getLevel()); }

		inline bool isVerbose() const {
			MutexLockerRead mL(&this->configMux);
			return (this->minLevel >= LOG_VERBOSE);
		 }
		inline bool isDebug() const {
			MutexLockerRead mL(&this->configMux);
			return (this->minLevel >= LOG_DEBUG);
		 }

		/**
		 * @brief   Evaluate if the log-context (including indentation) will be printed. Reentrant.
		 * @return  true when log-context is passed to LogHanlder; false else (NULL will be passed)
		 */
		inline bool isContextPrintEnabled() const {
			MutexLockerRead mL(&this->configMux);
			return this->printContext;
		 }

		/**
		 * @brief   Evaluate if the LogContext has an associated LogHandler. Reentrant.
		 * @return  true when LogHandler reference is valid; false else
		 */
		inline bool hasHandler() const {
			MutexLockerRead mL(&this->configMux);
			return (this->handler != NULL);
		 }
		/**
		 * @brief   Get ID of current (associated) LogHandler. Reentrant.
		 * @return  0 when LogHandler is NULL, handler's ID else
		 */
		long getHandlerID() const;

		static DataFieldCollector<LogContext>* getDataFields();

//@}
	public:
		/**
		 * @brief   Get log-level as a literal string
		 * @param level [in] LogLevel to be retrieved as literal string
		 * @return  passed log level as literal string
		 */
		static const char* getLogLevelAsString( LogLevel level );
		/**
		 * @brief   Get log-level from a literal string
		 * @param level [in] LogLevel as literal string
		 * @return  log-level (as enum) associated with the argument 
		 */
		static LogLevel getLogLevelFromString( const char* level );

	protected:
		/**
		 * @brief   Create a (shadow) copy of log-context name with indentation.
		 */
		void rebuiltContext();

	protected:
		char context[_LogContext_CONTEXT_MAX_SIZE];	///< c string representing the log context (including indentation)
		std::string contextShadow;	///< real context name (without indentation), used when rebuilding context
		short indent;				///< indentation (number of spaces before context)
		LogLevel minLevel;			///< minumin level of logged entry, it may be a LogLevel mask (as LOG_ALL)
		bool printContext;			///< flag, default true, print the context in the log output
		LogHandler* handler;		///< output (renderer) handler
		mutable MutexRW configMux;	///< mutex of configuration (previous vars)

		/**
		 * @brief   Get instances of LogContext implementations. Meyer singleton.
		 * @return  static reference to instances' list. Allocated on first use (reentrant after that).
		 */
		static ListT<LogContext*>& getInstances();

		friend class wosh::SystemLogger;

	private:
		LogContext(const LogContext&) { }
		LogContext& operator=(const LogContext&) { return *this; }

}; // class def

}; // namespace wosh

#endif //__WOSH_Core_LogContext_H__
